
#include "Generator.h"

using namespace std;

Generator::Generator(int length, int temperature, int offset)
{
	this->temperatureBase = temperature;
	this->temperatureOffset = offset;
	srand( (unsigned)time( NULL ) );
	for(int i = 0; i < length; ++i)
	{
		switch(rand()%4)
		{
		case 0: 
			this->nucleotide.push_back(NUCLEOTIDE::A);
			break;
		case 1: 
			this->nucleotide.push_back(NUCLEOTIDE::C);
			break;
		case 2:
			this->nucleotide.push_back(NUCLEOTIDE::G);
			break;
		case 3:
			this->nucleotide.push_back(NUCLEOTIDE::T);
			break;
		}
	}
}

Generator::Generator(char* filename, int temperature, int offset)
{
	this->temperatureBase = temperature;
	this->temperatureOffset = offset;
	std::ifstream input;
	input.open(filename);

	while(!input.eof())
	{
		int loaded;
		input >> loaded;
		switch(loaded)
		{
			case 65: 
				this->nucleotide.push_back(NUCLEOTIDE::A);
				break;
			case 67: 
				this->nucleotide.push_back(NUCLEOTIDE::C);
				break;
			case 71:
				this->nucleotide.push_back(NUCLEOTIDE::G);
				break;
			case 84:
				this->nucleotide.push_back(NUCLEOTIDE::T);
				break;
			default:
				cout << "Invalid character in input file.\n";
		}
	}
}


Generator::~Generator(void)
{
}

void Generator::CreateInstance(string outputFile)
{
	cout << "Generating instance into file " << outputFile << "\n";

	cout << "Generated sequence\n";

	cout << this->GetKey(&(this->nucleotide)) << std::endl;

	this->GenerateGrid();

	for(std::map<string,int>::iterator i = this->grid.begin(); i != this->grid.end(); ++i)
	{
		cout << (*i).first << " " << (*i).second << std::endl;
	}

	std::ofstream out;
	out.open(outputFile);

	out << this->grid.size() << " " << this->nucleotide.size() << std::endl;

	this->PrintNucleo(&out, this->first);

	for(std::map<string,int>::iterator i = this->grid.begin(); i != this->grid.end(); ++i)
	{
		if((*i).first != this->first)
		{
			this->PrintNucleo(&out, (*i).first);
		}
	}

	out.close();
}

void Generator::PrintNucleo(std::ofstream *out, std::string key)
{
	int cnt = (float)this->grid[key];
		
	float probs[4];

	for(int i = 0; i < 4; ++i)
	{
		probs[i] = 0;
	}
	
	int nerrors = 0;

	for(int i = 0; i < cnt; ++i)
	{
		if(rand()%100 < ERROR)
			++nerrors;
	}

	float p = 100.0f;

	int cbeg;
	int cerrs;

	if(cnt - nerrors > 3)
	{
		nerrors = 0;
		cbeg = 3;
		cerrs = 0;
	}
	else
	{
		cbeg = cnt;
		if(cbeg > 3)
			cbeg = 3;
		cerrs = cbeg+nerrors-cnt;
	}
	if(cnt > 3)
		cnt = 3;

	for(int i = 1; i < cerrs+1; ++i)
	{
		float r = ((float)(rand()%150)) / 10.0f;

		p -= r;

		probs[cbeg+i] = r;
	}
	
	float r = ((float)(rand()%150)) / 10.0f;
	
	if(cbeg > 0)
		probs[cnt-1] = r;
	else
		r = 0;

	probs[cnt] = p - r;	

	(*out) << key << " " << probs[0] << " " << probs[1] << " " << probs[2] << " " << probs[3] << "\n";

}

int Generator::GetBindingEnergy(const NUCLEOTIDE& nucleo)
{
	if(nucleo == NUCLEOTIDE::A || nucleo == NUCLEOTIDE::T)
	{
		return AT_ENERGY;
	}
	else if(nucleo == NUCLEOTIDE::C || nucleo == NUCLEOTIDE::G)
	{
		return GC_ENERGY;
	}
	throw -1;
}

int Generator::GetBindingEnergy(const std::vector<NUCLEOTIDE>& nucleos)
{
	int energy = 0;

	for(std::vector<NUCLEOTIDE>::const_iterator i = nucleos.begin(); i < nucleos.end(); ++i)
	{
		energy += this->GetBindingEnergy(*i);
	}

	return energy;
}

void Generator::GenerateGrid()
{
	bool isFirst = true;

	std::vector<NUCLEOTIDE> current;
	std::string previous;

	this->grid.clear();

	for(std::vector<NUCLEOTIDE>::iterator i = this->nucleotide.begin(); i < this->nucleotide.end(); ++i)
	{
		current.clear();

		std::vector<NUCLEOTIDE>::iterator j;
		for(j = i; this->GetBindingEnergy(current) < this->temperatureBase - this->temperatureOffset; ++j)
		{
			if(j == this->nucleotide.end())
				break;

			current.push_back(*j);
		}

		while(this->GetBindingEnergy(current) <= this->temperatureBase + this->temperatureOffset)
		{
			if(this->GetBindingEnergy(current) < this->temperatureBase - this->temperatureOffset)
				break;

			std::map<string,int>::iterator k = this->grid.find(this->GetKey(&current));

			if(k == this->grid.end())
			{
				if(isFirst || this->GetKey(&current).length() >= previous.length() - 1)
				{
					this->grid[this->GetKey(&current)] = 1;
					previous = this->GetKey(&current);
				}

				if(isFirst)
				{
					this->first = this->GetKey(&current);
					isFirst = false;
				}
			}
			else
			{
				if(this->GetKey(&current).length() >= previous.length() - 1)
				{
					++(this->grid[this->GetKey(&current)]);
					previous = this->GetKey(&current);
				}
			}

			if(j >= this->nucleotide.end())
				break;

			current.push_back(*j);

			++j;

			
		} 
	}
}

std::string Generator::GetKey(std::vector<NUCLEOTIDE> *nucleo)
{
	std::string result = "";
	for(std::vector<NUCLEOTIDE>::iterator i = nucleo->begin(); i < nucleo->end(); ++i)
	{
		string addon;

		switch((*i))
		{
		case NUCLEOTIDE::A:
			addon = "A";
			break;
		case NUCLEOTIDE::C:
			addon = "C";
			break;
		case NUCLEOTIDE::G:
			addon = "G";
			break;
		case NUCLEOTIDE::T:
			addon = "T";
			break;
		}
		result += addon;
	}

	return result;
}


